package org.jeecg.modules.paycenter.channel.tonglian;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.HttpTools;
import org.jeecg.common.util.encryption.AESUtil;
import org.jeecg.common.util.encryption.RSAUtil;
import org.jeecg.modules.paycenter.exception.BankReceiveException;
import org.jeecg.modules.paycenter.exception.BankSendException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class TongLianUtil {
    private static Logger logger = LoggerFactory.getLogger("bankLog");
    //region
    public static final long _orderExpireDatetime = 60 * 24; //订单有效期24小时，  订单过期时间, 分钟
    private static String _merCode = null;
    private static String  _baseUrl = null;
    private static String _aesPrivateKey = null;
    private static String  _privateKey = null;
    private static  String  _pubKey = null;
    private static  String  _ip = null;
    private static  String  _backUrl = null;
    private static  String  _frontUrl = null;
    private static  boolean  _isUsed = false;

    public static void initParams(boolean isUsed, Map<String,String> params){
        _merCode = params.get("merCode");
        _baseUrl = params.get("url");
        _aesPrivateKey = params.get("aesPrivateKey");
        _privateKey = params.get("privateKey");
        _pubKey = params.get("pubKey");
        _ip = params.get("ip");
        _backUrl = params.get("backUrl");
        _frontUrl = params.get("frontUrl");

        _isUsed = isUsed;
    }

    public static String decryptAES(String text){
        return AESUtil.decryptAES(text, _aesPrivateKey);
    }

    private static JSONObject send(String url, String jsonString) {
        try {
            logger.info("发送数据：" + jsonString);
            String ret = HttpTools.postJson(_baseUrl + url, jsonString);
            logger.info("接收数据：" + ret);
            JSONObject jsonObject = JSONObject.parseObject(ret, Feature.OrderedField);
//            if(isSuccess(jsonObject)) {
//                if (!verifySign(jsonObject)) {
//                    throw new JeecgBootException("交易失败:签名验证失败");
//                }
//            }
            return jsonObject;
        } catch (ConnectException e) {
            throw new BankSendException("交易失败:发起交易请求超时");
        } catch (SocketTimeoutException e){
            throw new BankReceiveException("交易失败:发起交易请求超时");
        } catch (Exception e) {
            throw new JeecgBootException(e.getMessage());
        }
    }

    public static boolean verifySign(TreeMap<String, String>  data) throws Exception {
        if(!data.containsKey("sign")){
            return false;
        }
        String sign = data.get("sign");
        if(StringUtils.isEmpty(sign)){
            return false;
        }
        data.remove("sign");
        StringBuilder sb= new StringBuilder();
        for (Map.Entry<String, String> entry : data.entrySet()) {
            if (entry.getValue() != null && entry.getValue().length() > 0) {
                sb.append(entry.getKey()).append("=")
                        .append(entry.getValue()).append("&");
            }
        }
        String dataStr = sb.deleteCharAt(sb.length()-1).toString();
        return RSAUtil.verifySign_sha1(dataStr, sign, _pubKey );
    }

    public static TreeMap<String,String> beanToTreeMap(Object object){
        Map<String, Object> middleRetData = BeanUtil.beanToMap(object);
        TreeMap<String, String> map = new TreeMap<>();
        for (Map.Entry<String, Object> entry : middleRetData.entrySet()) {
            map.put(entry.getKey(), String.valueOf(entry.getValue()));
        }
        return map;
    }

    public static boolean verifySign(JSONObject jo) throws Exception {
        JSONObject data = jo.getJSONObject("data");
        if(data == null){
            return false;
        }
        if(!data.containsKey("sign")){
            return false;
        }
        String sign = data.getString("sign");
        if(StringUtils.isEmpty(sign)){
            return false;
        }
        Set<String> strings = new TreeSet(data.keySet());
        strings.remove("sign");
        StringBuilder sb= new StringBuilder();
        for (String string : strings) {
            String v = data.getString(string);
            if(StringUtils.isNotEmpty(v)) {
                sb.append(string);
                sb.append("=");
                sb.append(v);
                sb.append("&");
            }
        }
        String dataStr = sb.deleteCharAt(sb.length()-1).toString();
        return RSAUtil.verifySign_sha1(dataStr, sign, _pubKey );
    }

    private static String getParams(JSONObject data){
        data.put("merCode",_merCode);
        data.put("timestamp", DateUtils.getDateStr());
        data.put("version","1.0");
        data.put("signType","RSA");

        Set<String> strings = new TreeSet(data.keySet());
        StringBuilder sb= new StringBuilder();
        for (String key : strings) {
            String v = data.getString(key);
            if(StringUtils.isNotEmpty(v)){
                sb.append(key);
                sb.append("=");
                sb.append(v);
                sb.append("&");
            }
        }

        String dataStr = sb.deleteCharAt(sb.length()-1).toString();
        String sign = RSAUtil.sign_sha1(dataStr,_privateKey);
        data.put("sign",sign);
        return JSON.toJSONString(data);
    }

    public static boolean isSuccess(JSONObject data){
        if(data == null) {
            return false;
        }
        if("0".equals( data.getString("code"))){
            return true;
        }
        return false;
    }
    public static String getMsg(JSONObject data){
        if(data == null) {
            return "";
        }
        return data.getString("msg");
    }

    // endregion

    //region 会员相关
    public static  JSONObject createPersonMember(String bizUserId , String name , String identityNo , String memberType,String phone , String carNo ,String source) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("source", source);//1：手机 2：PC
        data.put("name", name);
        data.put("identityType", "1");
        data.put("identityNo", AESUtil.encryptAES(identityNo, _aesPrivateKey)); //证件号   AES 加密
        data.put("memberType", memberType); //会员类型（1：货主 2：司机 3：其他）
        data.put("phone", phone);
        if ("2".equals(memberType)) {
            if (StringUtils.isEmpty(carNo)) {
                throw new JeecgBootException("车牌号不能为空");
            }
            data.put("carNo", carNo);//车牌号（memberType为2时必填）
        }
        String params = getParams(data);
        return send("/member/na/createPersonMember", params);
    }

    public static  JSONObject getMemberInfo(String bizUserId) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        String params = getParams(data);
        return send("/member/na/getMemberInfo", params);
    }

    public static  JSONObject createCompanyMember(String bizUserId , String memberType, String source , JSONObject companyBasicInfo) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("source", source);
        data.put("memberType", memberType); //会员类型（1：货主 2：司机 3：其他）
        data.put("companyBasicInfo", companyBasicInfo);

        String params = getParams(data);
        return send("/member/na/createCompanyMember", params);
    }

    //影印件上传
    public static  JSONObject idcardCollect(String bizUserId , String picType, String picture , String ocrComparisonResultBackUrl ) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
//        1：营业执照
//        8：身份证正面（人像面）
//        9：身份证反面（国徽面）
//        10：道路运输经营许可证/道路运输证
//        11：从业资格证
        data.put("picType", picType);//
        data.put("picture", picture);
        if(StringUtils.isNotEmpty(ocrComparisonResultBackUrl)) {
            data.put("ocrComparisonResultBackUrl", ocrComparisonResultBackUrl);
        }
        String params = getParams(data);
        return send("/member/na/idcardCollect", params);
    }

//    public static  JSONObject ocrComparisonResult(String bizUserId) {
//        JSONObject data = new JSONObject();
//        data.put("bizUserId", bizUserId);//会员号
//        String params = getParams(data);
//        return send("/member/na/ocrComparisonResult", params);
//    }



    /**
     * 绑定/解绑手机申请
     * @param bizUserId
     * @param phone
     * @param verificationCodeType 验证码类型 9-绑定手机;6-解绑手机
     * @return
     */
    public static  JSONObject sendVerificationCode(String bizUserId , String phone ,String verificationCodeType) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("phone", phone);
        data.put("verificationCodeType", verificationCodeType);  //验证码类型 9-绑定手机;6-解绑手机
        String params = getParams(data);
        return send("/member/na/sendVerificationCode", params);
    }

    /**
     * 绑定手机确认
     * @param bizUserId
     * @param phone
     * @param verificationCode  验证码
     * @return
     */
    public static  JSONObject bindPhone(String bizUserId , String phone ,String verificationCode) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("phone", phone);
        data.put("verificationCode", verificationCode);  //验证码
        String params = getParams(data);
        return send("/member/na/bindPhone", params);
    }

    /**
     * 解绑手机
     * @param bizUserId
     * @param phone
     * @param verificationCode
     * @return
     */
    public static  JSONObject unbindPhone(String bizUserId , String phone ,String verificationCode) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("telephone", phone);
        data.put("verificationCode", verificationCode);  //验证码
        String params = getParams(data);
        return send("/member/na/unbindPhone", params);
    }

    /**
     * 绑定对公银行卡
     * @param bizUserId
     * @param name
     * @param accountNo
     * @param parentBankName
     * @param bankName
     * @param unionBank
     * @return
     */
    public static  JSONObject applyBindGHBankCard(String bizUserId , String name, String accountNo, String parentBankName, String bankName, String unionBank , String accountSetNo) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("type", "2"); //卡类型（1：对私 2：对公）
        data.put("bindType", "1");  //绑卡方式（1：收款 2：收付款）（卡类型=1时必填，卡类型=2时不能选择绑卡方式2）
        data.put("name", name); //企业会员为法人姓名
        data.put("accountNo", AESUtil.encryptAES(accountNo, _aesPrivateKey));
//        data.put("accountSetNo", accountSetNo);
        data.put("parentBankName", parentBankName);
        data.put("bankName", bankName);
        data.put("unionBank", unionBank);//支付行号

        String params = getParams(data);
        return send("/member/na/applyBindBankCard", params);
    }

    /**
     * 绑定银行卡确认 , 绑定方式=2(收付款)时，调用
     * @param bizUserId
     * @param tranceNum
     * @param phone
     * @param verificationCode
     * @return
     */
    public static  JSONObject confirmBindBankCard(String bizUserId , String tranceNum, String phone, String verificationCode) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("tranceNum", tranceNum);
        data.put("phone", phone);
        data.put("verificationCode", verificationCode);

        String params = getParams(data);
        return send("/member/na/confirmBindBankCard", params);
    }

    /**
     * 绑定个人银行卡
     * @param bizUserId
     * @param name
     * @param identityNo
     * @param phone
     * @param cardNo
     * @return
     */
    public static  JSONObject applyBindPersonBankCard(String bizUserId , String name ,String identityNo,String phone,String cardNo) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("type", "1"); //卡类型（1：对私 2：对公）
        data.put("bindType", "1");  //绑卡方式（1：收款 2：收付款）（卡类型=1时必填，卡类型=2时不能选择绑卡方式2）
        data.put("name", name);
        data.put("identityType", "1");
        data.put("identityNo", AESUtil.encryptAES(identityNo, _aesPrivateKey));
        data.put("phone", phone);
        data.put("cardNo", AESUtil.encryptAES(cardNo, _aesPrivateKey));

        String params = getParams(data);
        return send("/member/na/applyBindBankCard", params);
    }

    /**
     * 解绑银行卡
     * @param bizUserId
     * @param cardNo
     * @return
     */
    public static  JSONObject unbindBankCard(String bizUserId ,String cardNo) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号
        data.put("cardNo", AESUtil.encryptAES(cardNo, _aesPrivateKey));

        String params = getParams(data);
        return send("/member/na/unbindBankCard", params);
    }

    public static  JSONObject signContract(String bizUserId ,String signAcctName,String jumpPageType,String jumpUrl,String noContractUrl,String source) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号

        data.put("signAcctName", signAcctName);//签约户名 (个人会员：名称 企业会员：法人提现，则上送“法人姓名” 对公户提现，则上送“企业名称”)
        data.put("jumpPageType", jumpPageType); //1：H5 页面 2：小程序
        if(StringUtils.isNotEmpty(jumpUrl)) {
            data.put("jumpUrl", jumpUrl); //签订成功后，跳转返回的页面地址（仅支持https）
        }
        if(StringUtils.isNotEmpty(noContractUrl)) {
            data.put("noContractUrl", noContractUrl);//会员取消签约，跳转返回的页面地址（仅支持https）
        }
        data.put("source", source);
        String params = getParams(data);
        return send("/member/na/signContract", params);
    }



    // endregion

    //region 支付相关

    /**
     * 查询账户余额，账户类型为1查询平台账户余额；账户类型为2查询会员余额，会员号必填
     * @param bizUserId 会员号 账户类型为2必填
     * @param accountType 账户类型:1-平台账户2-会员账户
     * @return
     */
    public static  JSONObject queryBalance(String bizUserId ,String accountType) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号 账户类型为2必填
        data.put("accountType", accountType); //账户类型:1-平台账户2-会员账户

        String params = getParams(data);
        return send("/member/na/queryBalance", params);
    }

    //账户收支明细
    public static  JSONObject ystQueryInExpDetail(String bizUserId ,String dateStart,String dateEnd,String cursor,String tradeType) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);//会员号 （若平台，上送固定值：#yunBizUserId_B2C#）
        data.put("dateStart", dateStart);
        data.put("dateEnd", dateEnd);
//        data.put("startPosition", startPosition);
//        data.put("queryNum", queryNum);
        if(StringUtils.isNotEmpty(cursor)) {
            data.put("cursor", cursor);
        }
        if(StringUtils.isNotEmpty(tradeType)) {
            data.put("tradeType", tradeType);
        }

        String params = getParams(data);
        return send("/ystorder/na/ystQueryInExpDetail", params);
    }

    //用户余额明细查询
//    public static  JSONObject queryBalanceDetail(String bizUserId ,String accountType) {
//        JSONObject data = new JSONObject();
//        data.put("bizUserId", bizUserId);//会员号 账户类型为2必填
//        data.put("accountType", accountType); //账户类型。1-货主余额账户
//        String params = getParams(data);
//        return send("queryBalanceDetail", params);
//    }


    //消费
    public static  JSONObject ystConsumeApply(String merOrderNo , String payerId, String frontUrl, String backUrl, String amount, String summary,String source ,Long ExpireDatetime) {
        JSONObject data = new JSONObject();
        data.put("merOrderNo", merOrderNo);
        data.put("payerId", payerId);
        data.put("recieverId", "#yunBizUserId_B2C#");
        data.put("frontUrl", _frontUrl + frontUrl);
        data.put("backUrl", _backUrl + backUrl);
        data.put("orderExpireDatetime", ExpireDatetime);

        JSONObject payMethod = new JSONObject();
        JSONObject GATEWAY_VSP = new JSONObject();
        GATEWAY_VSP.put("amount", amount);
        GATEWAY_VSP.put("paytype", "B2C");
        payMethod.put("GATEWAY_VSP",GATEWAY_VSP);

        data.put("payMethod", payMethod.toJSONString());

        data.put("amount", amount);
        data.put("fee", "0");
        data.put("industryCode", "1929");
        data.put("industryName", "物流");
        data.put("consumerIp", _ip);
        data.put("summary", summary);
        data.put("source", source);

        String params = getParams(data);
        return send("/ystorder/na/ystConsumeApply", params);
    }

    //订单关闭
    public static  JSONObject ystCloseOrder(String merOrderNo) {
        JSONObject data = new JSONObject();
        data.put("merOrderNo", merOrderNo);
        String params = getParams(data);
        return send("/ystorder/na/ystCloseOrder", params);
    }

    //付款
    public static  JSONObject ystPay(String merOrderNo , String withdrawUserId, String backUrl, String amount, String bankCardPro, String bankCardNo, String logisticsNo,String source) {
        JSONObject data = new JSONObject();
        data.put("merOrderNo", merOrderNo);
        data.put("withdrawUserId", withdrawUserId);//提现用户会员号
        data.put("amount", amount);
        data.put("fee", "0");
        data.put("bankCardPro", bankCardPro); //账户属性：0：个人银行卡 1：企业对公账户
        data.put("bankCardNo", AESUtil.encryptAES(bankCardNo, _aesPrivateKey));
        data.put("withdrawType", "D0"); //提现方式： D0：D+0 到账 D1：D+1 到账
        data.put("logisticsNo", logisticsNo); //物流号
        data.put("industryCode", "1929");
        data.put("industryName", "物流");
        data.put("source", source);
        data.put("backUrl", _backUrl + backUrl);

        String params = getParams(data);
        return send("/ystorder/na/ystPay", params);
    }

    //提现
    public static  JSONObject ystWithdrawApply(String merOrderNo , String bizUserId, String backUrl, String amount, String bankCode, String bankCardPro, String bankCardNo,String source) {
        JSONObject data = new JSONObject();
        data.put("merOrderNo", merOrderNo);
        data.put("bizUserId", bizUserId);
        data.put("amount", amount);
        data.put("fee", "0");
//        data.put("bankCode", bankCode);
        data.put("bankCardPro", bankCardPro); //账户属性：0：个人银行卡 1：企业对公账户
        data.put("bankCardNo", AESUtil.encryptAES(bankCardNo, _aesPrivateKey));
        data.put("withdrawType", "D0"); //提现方式： D0：D+0 到账 D1：D+1 到账
        data.put("backUrl", backUrl);
        data.put("industryCode", "1929");
        data.put("industryName", "物流");
        data.put("source", source);

        String params = getParams(data);
        return send("/ystorder/na/ystWithdrawApply", params);
    }

    //订单状态查询
    public static  JSONObject ystOrderStatusQuery(String merOrderNo) {
        JSONObject data = new JSONObject();
        data.put("merOrderNo", merOrderNo);
        String params = getParams(data);
        return send("/ystorder/na/ystOrderStatusQuery", params);
    }

    public static  JSONObject getOrderDetail(String merOrderNo) {
        JSONObject data = new JSONObject();
        data.put("merOrderNo", merOrderNo);

        String params = getParams(data);
        return send("/ystorder/na/getOrderDetail", params);
    }



    //电子回单下载
    public static  JSONObject ystEleReceiptDownload(String merOrderNo) {
        JSONObject data = new JSONObject();
        data.put("merOrderNo", merOrderNo);

        String params = getParams(data);
        return send("/ystorder/na/ystEleReceiptDownload", params);
    }

    //退款
    public static  JSONObject ystRefund(String bizUserId , String merOrderNo, String oriMerOrderNo, String backUrl, String amount) {
        JSONObject data = new JSONObject();
        data.put("bizUserId", bizUserId);
        data.put("merOrderNo", merOrderNo);
        data.put("oriMerOrderNo", oriMerOrderNo);
        data.put("refundType", "D0"); //D0：D+0实时向渠道发起退款； D1：D+1 14:30 向渠道发起退款
        data.put("backUrl", backUrl);
        data.put("amount", amount);
        data.put("feeAmount", "0");
        String params = getParams(data);
        return send("/ystorder/na/ystRefund", params);
    }
    //商户集合对账文件下载
    public static  JSONObject ystGetCheckAccountFile(String date , String fileType) {
        JSONObject data = new JSONObject();
        data.put("date", date);
        data.put("fileType", fileType);//文件类型（1-明细2-汇总3-excel 对账单（含汇总及明细,文件上盖有清结算章）默认为 1）

        String params = getParams(data);
        return send("/ystorder/na/ystGetCheckAccountFile", params);
    }


    // endregion

    public static String yuanToFen(BigDecimal amt) {
        BigDecimal amount = amt.multiply(new BigDecimal(100));
        return amount.intValue() + "";
    }
}
